using System;
using System.ComponentModel;
using System.Globalization;
using System.Net.Mail;
using System.Runtime.Serialization;
using System.Xml.Serialization;

namespace ICalendar.CalendarProperties
{
    /// <summary>
    /// The property defines an "Attendee" within a calendar component.
    /// </summary>
    [Serializable]
    [XmlRoot("attendee")]
    //[XmlElement("attendee")]
    public class Attendee : CalendarPropertyBase<MailAddress> 
    {
        private static readonly string[] AttendeeParameters = new[] { ReservedNames.ParameterCalendarUserSpecified, "MEMBER", "ROLE", "PARTSTAT", "RSVP", ReservedNames.ParameterDelegatedTo, ReservedNames.ParameterDelegatedFrom, "SENT-BY", ReservedNames.ParameterCommonName, "DIR", ReservedNames.ParameterLanguage };
        
        #region Properties
        ///// <summary>
        ///// This value type is used to identify properties that contain a calendar user address. 
        ///// </summary>
        ////public Uri Address { get; set; }
        ///// <summary>
        ///// The property parameter CN is for the common or displayable name associated with the calendar address.
        ///// </summary>
        ////public string DisplayName { get; set; }
        ///// <summary>
        ///// This value type is used to identify properties that contain a calendar user address. 
        ///// The property parameter CN is for the common or displayable name associated with the calendar address.
        ///// </summary>
        //[XmlIgnore]
        //public MailAddress MailAddress { get; set; }
        /// <summary>
        /// For the intended role that the attendee will have in the calendar component.
        /// A recipient delegated a request MUST inherit the RSVP and ROLE values from the attendee that delegated the request to them
        /// </summary>
        [XmlIgnore]
        public Role Role { get; set; }
        /// <summary>
        /// For indicating whether the favor of a reply is requested, the default is false
        /// </summary>
        /// <value>The default value is false</value>
        /// <remarks>
        /// This parameter can be specified on properties with a CAL-ADDRESS value type. The parameter identifies the expectation of a reply from the calendar user specified by the property value. This parameter is used by the "Organizer" to request a participation status reply from an "Attendee" of a group scheduled event or to-do. If not specified on a property that allows this parameter, the default value is FALSE.
        /// </remarks>
        public bool IsReplyRequested { get; set; }
        /// <summary>
        /// For the status of the attendee's participation; RSVP, for indicating whether the favor of a reply is requested.
        /// </summary>
        [DefaultValue(ParticipationStatus.NeedsAction)]
        public ParticipationStatus ParticipationStatus { get; set; }
        /// <summary>
        /// To indicate the type of calendar user.
        /// </summary>
        public CalendarUserType CalendarUserType { get; set; }
        /// <summary>
        /// To indicate the groups that the attendee belongs to.
        /// </summary>
        [XmlIgnore]
        public MailAddressCollection Members { get; set; }
        /// <summary>
        /// DELEGATED-TO, to indicate the calendar users that the original request was delegated to.
        /// </summary>
        [XmlIgnore]
        public MailAddressCollection DelegatedTo { get; set; }
        /// <summary>
        /// DELEGATED-FROM, to indicate whom the request was delegated from.
        /// </summary>
        [XmlIgnore]
        public MailAddressCollection DelegatedFrom { get; set; }
        /// <summary>
        ///  SENT-BY, to indicate whom is acting on behalf of the ATTENDEE; 
        /// </summary>
        [XmlIgnore]
        public MailAddress SentBy { get; set; }
        /// <summary>
        /// and DIR, to indicate the URI that points to the directory information corresponding to the attendee
        /// </summary>
        [XmlIgnore]
        public Uri DirectoryEntryReference { get; set; }
        /// <summary>
        /// If the LANGUAGE property parameter is specified, the identified language applies to the CN parameter.
        /// </summary>
        public CultureInfo Language { get; set; }
        /// <summary>
        /// 
        /// </summary>
        //public Dictionary<string, string> CustomXParameters { get; set; }
        #endregion

        private Attendee()
        {
            
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="mailAddress">This value type is used to identify properties that contain a calendar user address. </param>
        public Attendee(MailAddress mailAddress)
        {
            if (mailAddress == null)
            {
                throw new ArgumentNullException("mailAddress");
            }

            this.Value = mailAddress;

            //Default: Required
            this.Role = Role.Required;
            //Default: False
            this.IsReplyRequested = false;
            //Default: NeedsAction
            this.ParticipationStatus = ICalendar.ParticipationStatus.NeedsAction;
            //Default: Individual
            this.CalendarUserType = CalendarUserType.Individual;
            this.Members = new MailAddressCollection();
            this.Language = CultureInfo.CurrentCulture;
            this.DelegatedFrom = new MailAddressCollection();
            this.DelegatedTo = new MailAddressCollection();
        }

        public Attendee(string mail) : this(new MailAddress(mail))
        {
        }

        protected Attendee(SerializationInfo info, StreamingContext context)
        {
            if (info == null)
                throw new ArgumentNullException("info");
          
            this.Value =  Util.FromCalAddressAndCommonName(info.GetString("ATTENDEE"),info.GetString(ReservedNames.ParameterCommonName));
            this.CalendarUserType = info.GetString(ReservedNames.ParameterCalendarUserSpecified) != null ? (CalendarUserType)Enum.Parse(typeof(CalendarUserType), info.GetString(ReservedNames.ParameterCalendarUserSpecified),true) : ICalendar.CalendarUserType.Individual;
            this.Members.FromCalAddressCollectionString(info.GetString("MEMBER"));
            this.Role.FromName(info.GetString("ROLE"));
            this.ParticipationStatus = ParticipationStatus.FromParticipationStatusName(info.GetString("PARTSTAT"));
            this.IsReplyRequested = info.GetBoolean("RSVP");
            this.DelegatedTo.FromCalAddressCollectionString(info.GetString(ReservedNames.ParameterDelegatedTo));
            this.DelegatedFrom.FromCalAddressCollectionString(info.GetString(ReservedNames.ParameterDelegatedFrom));
            this.SentBy = info.GetString("SENT-BY").FromCalAddress();
            var tempDIR = info.GetString("DIR");
            if (tempDIR != null)
            {
                this.DirectoryEntryReference = new Uri(tempDIR.Replace("\"", ""));
            }
            this.Language =  CultureInfo.GetCultureInfo(info.GetString(ReservedNames.ParameterLanguage));
        }
        
        protected override void SetParametersFromXParameters(XParameterCollection xparams)
        {
            if (xparams == null)
                return;

            this.Value = Util.FromCalAddressAndCommonName(GetValue(), xparams.GetValue(ReservedNames.ParameterCommonName));
            this.CalendarUserType = !string.IsNullOrEmpty(xparams.GetValue(ReservedNames.ParameterCalendarUserSpecified)) ? (CalendarUserType)Enum.Parse(typeof(CalendarUserType), xparams.GetValue(ReservedNames.ParameterCalendarUserSpecified),true) : ICalendar.CalendarUserType.Individual;
            this.Members.FromCalAddressCollectionString(xparams.GetValue("MEMBER"));
            this.Role.FromName(xparams.GetValue("ROLE"));
            this.ParticipationStatus = ParticipationStatus.FromParticipationStatusName(xparams.GetValue("PARTSTAT"));
            this.IsReplyRequested = Convert.ToBoolean(xparams.GetValue("RSVP"));
            this.DelegatedTo.FromCalAddressCollectionString(xparams.GetValue(ReservedNames.ParameterDelegatedTo));
            this.DelegatedFrom.FromCalAddressCollectionString(xparams.GetValue(ReservedNames.ParameterDelegatedFrom));
            this.SentBy = xparams.GetValue("SENT-BY").FromCalAddress();
            var tempDIR = xparams.GetValue("DIR");
            if (tempDIR != null)
            {
                this.DirectoryEntryReference = new Uri(tempDIR.Replace("\"", ""));
            }

            var language = xparams.GetValue(ReservedNames.ParameterLanguage);
            if (language != null)
            {
                this.Language = CultureInfo.GetCultureInfo(language);
            }
        }

        protected override XParameterCollection GetXParametersFromParameters()
        {
            return new XParameterCollection
                       {
                           {
                               ReservedNames.ParameterCalendarUserSpecified,
                               this.CalendarUserType != ICalendar.CalendarUserType.Individual
                                   ? this.CalendarUserType.ToString().ToUpperInvariant()
                                   : null
                               },
                           {"MEMBER", this.Members.GetCalAddressCollectionString()},
                           {"ROLE", this.Role.GetName()},
                           {"PARTSTAT",ParticipationStatus.GetParticipationStatusName()},
                           {"RSVP", IsReplyRequested.ToString().ToUpperInvariant()},
                           {ReservedNames.ParameterDelegatedTo, this.DelegatedTo.GetCalAddressCollectionString()},
                           {ReservedNames.ParameterDelegatedFrom, this.DelegatedFrom.GetCalAddressCollectionString()},
                           {"SENT-BY", string.Format("\"{0}\"", this.SentBy.GetCalAddress())},
                           {ReservedNames.ParameterCommonName, this.Value.GetCommonName()},
                           {"DIR", string.Format("\"{0}\"", this.DirectoryEntryReference)},
                           {ReservedNames.ParameterLanguage, Language.Name}
                       };         
        }

        protected override string[] GetXReservedParametersNames()
        {
            return AttendeeParameters;
        }

        protected override void SetValue(string value)
        {            
            this.Value = value.Replace("mailto:", "").FromCalAddress();
        }

        protected override string GetValue()
        {
            return Value.GetCalAddress();
        }

        #region ISerializable Members

        public new void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (info == null)
                throw new ArgumentNullException("info");

            info.AddValue("VALUE",  Value.GetCalAddress());
            info.AddValue(ReservedNames.ParameterCalendarUserSpecified, this.CalendarUserType != ICalendar.CalendarUserType.Individual
                                        ? this.CalendarUserType.ToString().ToUpperInvariant()
                                        : null);
            info.AddValue("MEMBER", Members.GetCalAddressCollectionString());
            info.AddValue("ROLE", this.Role.GetName());
            info.AddValue("PARTSTAT", ParticipationStatus.GetParticipationStatusName());
            info.AddValue("RSVP", IsReplyRequested.ToString().ToUpperInvariant());
            info.AddValue(ReservedNames.ParameterDelegatedTo, this.DelegatedTo.GetCalAddressCollectionString());
            info.AddValue(ReservedNames.ParameterDelegatedFrom, this.DelegatedFrom.GetCalAddressCollectionString());
            info.AddValue("SENT-BY", string.Format("\"{0}\"", this.SentBy.GetCalAddress()));
            info.AddValue(ReservedNames.ParameterCommonName, this.Value.GetCommonName());
            info.AddValue("DIR", string.Format("\"{0}\"", this.DirectoryEntryReference));
            info.AddValue(ReservedNames.ParameterLanguage, Language.Name);
        }

        #endregion
    }
}